#!/usr/bin/env python

"""
File:      server.py
Author:    tcodi
Warranty:  AS IS, NO GUARANTEES TO ANY EFFECT.
License:   Lesser GNU Public License
"""

import socket 
import select
import threading
import time
import bbs
import sys
import user

ServerAuth = "DeathByFlowers"
sman = bbs.bbs("messages.txt", ".passwd")
     
#http://chaos.weblogs.us/archives/164
#http://www.java2s.com/Tutorial/Python/0420__Network/BroadcastSender.htm
#http://www.java2s.com/Code/Python/Network/UDPBroadcastServer.htm
class BroadcastSender(threading.Thread):
    
    #a list of all active client threads.
    client = {}
    
    def __init__(self,host, udp_port, tcp_port):
        threading.Thread.__init__(self)
        self.host = host
        self.udp_port = udp_port
        self.tcp_port = tcp_port

    def run(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,socket.IPPROTO_UDP)
        s.bind((self.host, self.udp_port))
        s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

        #fancy port scanner!! 55555,55556,55557
        for port in range(55555, 55558):  
           #don't want to broadcast on my own port
           if port != self.udp_port:
                #wtf!?!?!This doesn't seem to work on the linux!!!
                #s.sendto(str(self.tcp_port), ('<broadcast>',port))
                s.sendto(str(self.tcp_port), ('127.0.0.1',port))

        print "Intial Broadcast Sent(",self.host,", ", self.udp_port,")...."

        while 1:
            try:         
                (msg, addr) = s.recvfrom(1024)
                print "Response from", addr ,": ", repr(msg) 
                self.client[addr] = Client(addr,msg)
                self.client[addr].start()
                
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                print "No response."
            
class Client(threading.Thread):
    
    def __init__(self,addr, port):
        threading.Thread.__init__(self)
        self.ip_addr = addr[0]
        self.port = port
        self.server_name = addr
        
    def run(self):
        tcp_c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        tcp_c.connect((self.ip_addr, int(self.port)))

        tcp_c.send(ServerAuth)        
        data = tcp_c.recv(1024)

        if data:
            print "msg rcv1: " + data   
            while (1):
                try:
                    tcp_c.send(sman.getData())
                    server_data = tcp_c.recv(1024)
                    if server_data:
                        #unnessary now??
                        #tcp_c.send("Please sir may I have another?")
                        #server_data = tcp_c.recv(1024)
                        #print "msg rcv: " #+ server_data
                        sman.addData(server_data, self.server_name)
                    time.sleep(10)
                except:
                    sman.purge(self.server_name)
                    tcp_c.close()
                    break;
        
class Server(threading.Thread):
    
    #a list of all active users.
    users ={}
    
    def __init__(self, addr, port, buffer):
        threading.Thread.__init__(self)
        self.ip_addr = addr
        self.tcp_port = port
        self.buffer_size = buffer
          
    def run(self):
        #http://www.doughellmann.com/PyMOTW/select/
        listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        listening_socket.setblocking(0)

        listening_socket.bind((self.ip_addr, self.tcp_port))
        print  'RAW BBS Server: (', self.ip_addr,',', self.tcp_port,')'

        listening_socket.listen(5)

        outputs = []
        inputs = [listening_socket]
        
          
        while inputs:
          
            #Waits for I/O being available for reading from any socket object.
            rlist, wlist, xlist = select.select( inputs, outputs, inputs )
            
            for  i in rlist:
                if i is listening_socket:
                    new_socket, addr = i.accept()
                    new_socket.setblocking(0)
                    inputs.append(new_socket)
                    print "new socket ",addr               
                else:
                    #check if the user has already been  
                    #created, if not make a new one.
                    if not i.getpeername() in self.users:
                        currentUser = self.users[i.getpeername()] = user.User(i.getpeername(),sman)
                    else:
                        currentUser = self.users[i.getpeername()] 
                              
                    i.send(currentUser.handleOutput())                     
                    data = i.recv(self.buffer_size)                    

                    if data:
                        currentUser.handleInput(data,i.getpeername())
                        if i not in outputs:
                            outputs.append(i)
                    else:
                        if i in outputs:
                            outputs.remove(i)
                            inputs.remove(i)
                            if currentUser.isServer():
                                print "purge"                                
                                sman.purge(currentUser.name)
                            print currentUser.name,"deleted..."
                            del self.users[currentUser.name]
                            i.close()
                    
            for s in xlist:
                print 'handling exceptional condition for'
                # Stop listening for input on the connection
                inputs.remove(s)
                if s in outputs:
                    outputs.remove(s)
                s.close()
            

        
#http://www.tutorialspoint.com/python/python_multithreading.htm        
if __name__=="__main__":
    
    if len(sys.argv) == 4:
        s = Server(sys.argv[1],int(sys.argv[2]),1024)    
        b = BroadcastSender(sys.argv[1],int(sys.argv[3]),int(sys.argv[2]))    
        s.start()
        b.start()
    else:
        print "Usage: python server.py [host_ip][tcp_port][udp_port]"
   
    #s = Server('127.0.0.1',55550,1024)
    #s = Server('127.0.0.1',55551,1024)
    #s = Server('127.0.0.1',55552,1024)
    #b = BroadcastSender('127.0.0.1',55555,55550)
    #b = BroadcastSender('127.0.0.1',55556,55551)
    #b = BroadcastSender('127.0.0.1',55557,55552)
    
    
    
    


